/*!
  @file server.h
  @copyright 2013 Kubota Lab. All rights resereved.
*/

#ifndef _NET_SERVER_H_
#define _NET_SERVER_H_

#include <boost/asio.hpp>
#include <boost/bind.hpp>

namespace net {

/*! 
 *  @brief  Session class used by Server
 *  @author Kyohei Otsu <kyon@ac.jaxa.jp>
 *  @date   2013
 *  
 *  detailed documentaiton for this class
 */

class Session {
  using boost::asio::ip::tcp;

  enum { MAX_LEN = 1024; }

 public:
  Server(boost::asio::ioservice &io_service) 
      : socket_(io_service)
  { }
  ~Server() { }

  void start() {
    socket_.async_read_some(boost::asio::buffer(data_, MAX_LEN),
        boost::bind(&session::handleRead, this,
            boost::asio::placeholders::error, 
            boost::asio::placeholders::bytes_transferred));
  }

  tcp::socket& socket() { return socket_; }

 private:
  void handleRead(const boost::system::error_code &error, size_t bytes_transferred) {
    if (!error) {
      boost::asio::async_write(socket_,
          boost::asio::buffer(data_, bytes_transferred),
          boost::bind(&session::handleWrite, this, boost::asio::placeholders::error));
    } else {
      delete this;
    }
  }

  void handleWrite(const boost::system::error_code &error) {
    if (!error) {
      socket_.async_read_some(boost::asio::buffer(data_, MAX_LEN),
          boost::bind(&session::handleRead, this, 
              boost::asio::placeholders::error, 
              boost::asio::placeholders::bytes_transferred));
    } else {
      delete this;
    }
  }

  tcp::socket socket_;
  char data_[MAX_LEN];
};


/*! 
 *  @brief  Server class
 *  @author Kyohei Otsu <kyon@ac.jaxa.jp>
 *  @date   2013
 *  
 *  detailed documentaiton for this class
 */

class Server {
  using boost::asio::ip::tcp;

 public:
  Server(boost::asio::io_service &io_service, short port) 
      : io_service_(io_service)
      , acceptor_(io_service, tcp::endpoint(tcp::v4(), port))
  { 
    startAccept();
  }

  ~Server() { }

 private:
  void startAccept() {
    Session *new_session = new session(io_service_);
    acceptor_.async_accept(new_session->socket(), 
        boost::bind(&server::handleAccept, this, new_session, boost::asio::placeholders::error));
  }

  void handleAccept(session* new_session, const boost::system::error_code &error) {
    if (!error) {
      new_session->start();
    } else {
      delete new_session;
    }

    startAccept();
  }

  boost::asio::io_service &io_service_;
  tcp::acceptor acceptor_;
};

} // namespace net

#endif
